import java.awt.Canvas;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.RenderingHints;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.awt.image.VolatileImage;
import java.util.ArrayList;
import java.util.Collections;
import javax.swing.JFrame;

public class legacyengine extends JFrame implements Runnable
{
	private static final long serialVersionUID = 2710995903091158720L;
	//Publics
	boolean calculatefps = true;
	boolean isRunning;
	double FrameLimit = game.framelimit;
	boolean Displayfps = game.showfps;
	static ArrayList<Sprite> spritelist = new ArrayList<Sprite>();
	static Canvas canvas = new Canvas();
	static int globalXorigin = 0;
	static int globalYorigin = 0;
	static int frameretries = 0;
	//Privates
	private static int resX = 800;
	private static int resY = 800;
	private static int wantedresX = 800;
	private static int wantedresY = 800;
	private static double scaleX = 1;
	private static double scaleY = 1;
	private static String resmode = "fixed";
	static boolean paused = false;
	static ArrayList<Thread> thredlist = new ArrayList<Thread>();
	long cycleTime;
	
	
	int frames = 0;
	double unprocessedSeconds = 0;
	long lastTime = System.nanoTime();
	double secondsPerTick = 1 / FrameLimit;
	int tickCount = 0;
	private Color Text1 = Color.yellow;
	
	
	private VolatileImage volatileImg;
	private Graphics graphics = null;
	Graphics2D g2d = null;
	private Color background = Color.BLACK;
	private static BufferStrategy buffer = null;
	private static BufferedImage bi = null;
	private GraphicsDevice gd =null;
	private GraphicsConfiguration gc =null;

	//Constructor
	public legacyengine(int width, int height)
	{
		resX = width; 
		resY = height;
		System.out.println("Render engine starting");
		this.setIgnoreRepaint( true );
		this.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
		updatesettings();
		canvas.setIgnoreRepaint( true );
		canvas.setSize(resX,resY);   
		// add a title
		this.setTitle(game.gamename + " - Nanoengine");
		if (resmode == "fixed"){
			this.setResizable(false);
		}
		// Add canvas to game window...
		this.add(canvas);
		this.pack();
		this.setVisible( true );
		// Create BackBuffer...
		canvas.createBufferStrategy(2);
		buffer = canvas.getBufferStrategy();
		// Get graphics configuration...
		GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
		gd = ge.getDefaultScreenDevice();
		gc = gd.getDefaultConfiguration();
		// Create off-screen drawing surface
		bi = gc.createCompatibleImage( resX, resY );
		bi.setAccelerationPriority(1);
		//--
		System.out.println("Graphics accelleration: "+bi.getCapabilities(gc).isAccelerated());
		System.out.println("NANOENGINE RENDER INITIATED (res: "+resX+"x"+resY+")"); 
		createBackBuffer();
		
		isRunning = true;
		game.onstart();
	}

	public void run() 
	{
		while (isRunning) 
		{
			Main.timekeeperreset();
			Main.timekeeper("Legacy Runthread started");
			//check if we still have focus
			if (!canvas.hasFocus() && game.forcefocus){
				paused = true;
			}
			long now = System.nanoTime();
			long passedTime = now - lastTime;
			lastTime = now;
			if (passedTime < 0) passedTime = 0;
			if (passedTime > 100000000) passedTime = 100000000;

			unprocessedSeconds += passedTime / 1000000000.0;

			boolean ticked = false;
			while (unprocessedSeconds > secondsPerTick) {
				//Main.logic.update();
				unprocessedSeconds -= secondsPerTick;
				ticked = true;
				tickCount++;
				if (tickCount % FrameLimit == 0) 
				{
					if(Displayfps)
						{
						System.out.println(frames + " fps");
						}
					lastTime += 1000;
					frames = 0;
				}
			}

			if (ticked) 
			{
				updatesprites();
				Main.timekeeper("sprites updated");
				game.onupdate();
				Main.timekeeper("game updated");
				render(graphics);
				Main.timekeeper("rendered");
				frames++;
			} else {
				try 
				{
					Thread.sleep(1);
				} catch (InterruptedException e) 
				{
					e.printStackTrace();
				}
			}
		}
	}
	public void updatesprites(){
		for(int i=0;i<spritelist.size();i++)
    	{
    		if (spritelist.get(i) != null)
    		{
    			Runnable task = spritelist.get(i);
    			Thread worker = new Thread(task);
    			worker.start();
    			worker.setPriority(Thread.MAX_PRIORITY);
    			thredlist.add(worker);
    		}
    		while (thredlist.get(0).isAlive()){
    			
    		}
    		thredlist.remove(0);
    	}
	}
	
	public void render(Graphics g) {
		frameretries = 0;
		// create the hardware accelerated image.
		//System.out.println("WAAASUP"); 
		
		graphics = null;
		// clear back buffer...
	//	g2d = bi.createGraphics();
		//g2d.setColor( background );
		//g2d.fillRect( 0, 0, resX, resY);
		graphics = buffer.getDrawGraphics();
		//graphics.drawImage( bi, 0, 0, null );
		buffer.show();
		createBackBuffer();
		// Main rendering loop. Volatile images may lose their contents. 
		// This loop will continually render to (and produce if neccessary) volatile images
		// until the rendering was completed successfully.
		do {
			frameretries ++;
			if (frameretries >1){
				System.out.println("frameretries is at: "+frameretries); 
			}
			// Validate the volatile image for the graphics configuration of this 
			// component. If the volatile image doesn't apply for this graphics configuration 
			// (in other words, the hardware acceleration doesn't apply for the new device)
			// then we need to re-create it.
			GraphicsConfiguration gc = this.getGraphicsConfiguration();
			int valCode = volatileImg.validate(gc);

			// This means the device doesn't match up to this hardware accelerated image.
			if(valCode==VolatileImage.IMAGE_INCOMPATIBLE){
				createBackBuffer(); // recreate the hardware accelerated image.
			}
			Graphics offscreenGraphics = volatileImg.getGraphics();   
			offscreenGraphics.setColor(Color.WHITE);
			offscreenGraphics.fillRect(0, 0, getWidth(), getHeight());
			offscreenGraphics.setColor(Color.BLACK);
			offscreenGraphics.drawLine(0, 0, 10, 10); // arbitrary rendering logic
			///-----------------------------------------------------------------------------------
			if (!paused)
			{
				for(int i=0;i<spritelist.size();i++)
				{
					if (spritelist.get(i) != null)
					{
						//get some attributes
						if(spritelist.get(i).rot != 0)
						{
							offscreenGraphics.translate((int)(spritelist.get(i).x+spritelist.get(i).Xoffset+globalXorigin), (int)(spritelist.get(i).y+spritelist.get(i).Yoffset+globalYorigin));
							((Graphics2D) offscreenGraphics).rotate(spritelist.get(i).rot);
							if (spritelist.get(i).Type == ""){
								offscreenGraphics.drawImage(Main.SpriteStore.getSprite(spritelist.get(i).picture1), 0, 0,null);
							}else{
								//it's a geosprite
								offscreenGraphics.setColor(spritelist.get(i).Col); //get the colour
								if (spritelist.get(i).Type == "rect"){
									//and it's a rectangle
									offscreenGraphics.fillRect((int)spritelist.get(i).x+globalXorigin,(int)spritelist.get(i).y, spritelist.get(i).Width, spritelist.get(i).Height); 
								}else if(spritelist.get(i).Type == "text"){
									//it's text
									offscreenGraphics.setFont( new Font( "Courier New", Font.PLAIN, 12 ) );
									offscreenGraphics.setColor( Color.GREEN );
									offscreenGraphics.drawString(spritelist.get(i).text, (int)spritelist.get(i).x, (int)spritelist.get(i).y );
								}else{
									// it's somthing else that hasn't been coded yet
								}
							}
							((Graphics2D) offscreenGraphics).rotate(-spritelist.get(i).rot);
							offscreenGraphics.translate(-(int)(spritelist.get(i).x+spritelist.get(i).Xoffset+globalXorigin), -(int)(spritelist.get(i).y+spritelist.get(i).Yoffset+globalYorigin));
						}else{
							if (spritelist.get(i).Type == "image"){
								offscreenGraphics.drawImage(Main.SpriteStore.getSprite(spritelist.get(i).picture1), (int)(spritelist.get(i).x+spritelist.get(i).Xoffset+globalXorigin), (int)(spritelist.get(i).y+spritelist.get(i).Yoffset+globalYorigin),null);
							}else{
								//it's a geosprite
								offscreenGraphics.setColor(spritelist.get(i).Col); //get the colour
								if (spritelist.get(i).Type == "rect"){
									//and it's a rectangle
									offscreenGraphics.fillRect((int)spritelist.get(i).x+globalXorigin,(int)spritelist.get(i).y, spritelist.get(i).Width, spritelist.get(i).Height);  
								}else if(spritelist.get(i).Type == "text"){
									//it's text
									offscreenGraphics.setFont( new Font( "Courier New", Font.PLAIN, 12 ) );
									offscreenGraphics.setColor( Color.GREEN );
									offscreenGraphics.drawString(spritelist.get(i).text,(int)Math.round(spritelist.get(i).x),(int)Math.round(spritelist.get(i).y));
								}else{
									// it's somthing else that hasn't been coded yet
								}
							}
						}
					}
				}
			}else{
				offscreenGraphics.setColor(Color.black);
				offscreenGraphics.fillRect( 0, 0, 1100, 500);
				offscreenGraphics.setColor(Color.green);
				offscreenGraphics.drawString(game.gamename, 200, 200);
				if(game.showmenupicture){
					offscreenGraphics.drawImage(Main.SpriteStore.getSprite(game.menupicture), 0,0,null);
				}
				offscreenGraphics.drawString("press ESC to begin", 300, 400);
			}
				
			
			///-----------------------------------------------------------------------------------
			// paint back buffer to main graphics
			//g2d.drawImage(volatileImg, 0, 0, this);
			//graphics = buffer.getDrawGraphics();
			graphics.drawImage(volatileImg, 0, 0, this);
			buffer.show();
		// Test if content is lost   
		} while(volatileImg.contentsLost());

	}

	// This method produces a new volatile image.
	private void createBackBuffer() {
		GraphicsConfiguration gc = getGraphicsConfiguration();
		volatileImg = gc.createCompatibleVolatileImage(resX, resY);
	}

	public void update(Graphics g) {
		paint(g);
	}
	
	//-----------^Game Render^------------V MenuRender V--------------------------------------------
	
	public void renderMenu()
	{
		try {
			graphics = null;
			// clear back buffer...
			g2d = bi.createGraphics();
			g2d.setColor( background );
			g2d.fillRect( 0, 0, resX, resY);
			g2d.setColor(Text1);
			//-----------ACTUAL RENDER CRAP GOES IN HERE---------
			g2d.drawString(game.gamename, 200, 200);
			if(game.showmenupicture){
			g2d.drawImage(Main.SpriteStore.getSprite(game.menupicture), 0,0,null);
			}
			g2d.drawString("press ESC to begin", 300, 400);
			//------------------------------------------------------
			// Blit image and flip...
			graphics = buffer.getDrawGraphics();
			graphics.drawImage( bi, 0, 0, null );
			if(!buffer.contentsLost()){
				//halp
			}
			buffer.show();
			// Do the waiting 	
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				Thread.yield();
			}
		} finally {
			// release resources
			if( graphics != null ) 
				graphics.dispose();
			if( g2d != null ) 
				g2d.dispose();
		}
	}
	static void addsprite(Sprite spritetobeadded)
	{
		spritelist.add(spritetobeadded);
		// --------MOST IMPORTANT LINE IN THE JOINT ---
	/*	Collections.sort(spritelist);
		// ------------MEET THE Z-BUFFER WIZARD!-------
		System.out.println("Spritelist now contains: ");
		System.out.println("Entry \tName \tZ-depth");
		for(int i=0;i<spritelist.size();i++)
		{
			System.out.println(i+"\t"+spritelist.get(i).Name+"\t"+spritelist.get(i).z);	
		}*/
	}
	static void removeSprite(Sprite spritetoberemoved)
	{
		spritelist.remove(spritetoberemoved);
		/*System.out.println("Spritelist now contains: ");
		System.out.println("Entry \tName \tZ-depth");	
		for(int i=0;i<spritelist.size();i++)
		{
			System.out.println(i+"\t"+spritelist.get(i).Name+"\t"+spritelist.get(i).z);	
		}*/
		// --------MOST IMPORTANT LINE IN THE JOINT ---
		Collections.sort(spritelist);
		// ------------MEET THE Z-BUFFER WIZARD!-------
	}
	synchronized public void ChangeRes(boolean fullscreen, int x, int y)
	{
		if(fullscreen)
		{
			gd.setFullScreenWindow(this);
			resX = gd.getDisplayMode().getWidth();
			resY = gd.getDisplayMode().getHeight();
			bi = gc.createCompatibleImage( resX, resY );
			System.out.println("Fullscreened, and res changed to "+ resX+ " "+ resY);
		}else{
			//should probs pause the game temporarily here
			if( gd.getFullScreenWindow() != null){gd.setFullScreenWindow(null);}
			resX = x;
			resY = y;
			bi = gc.createCompatibleImage( resX, resY );
			canvas.setSize(resX,resY);
			System.out.println("res changed to "+ resX+ " "+ resY);
			// Create BackBuffer...
			canvas.createBufferStrategy( 2 );
			buffer = canvas.getBufferStrategy();
			// Get graphics configuration....
			GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
			GraphicsDevice gd = ge.getDefaultScreenDevice();
			GraphicsConfiguration gc = gd.getDefaultConfiguration();
			// Create off-screen drawing surface
			bi = gc.createCompatibleImage( resX, resY );
			this.setSize(x, y);
		}//if they are going for the scale method
		if(resmode == "scale"){
			// find how much to scale the X
			scaleX = (double) resX / (double) wantedresX;
			scaleY = (double) resY / (double) wantedresY;
			System.out.println(resX+" "+wantedresX);
			System.out.println(scaleX + " "+ scaleY);
			
		}
	}
	public void updatesettings(){
		FrameLimit = game.framelimit;	
		Displayfps = game.showfps;
		wantedresX = game.DesiredresX;
		wantedresY = game.DesiredresY;
		resmode = game.resmode;
	}

	
	public void toggleAA() {
		if(g2d.getRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING) == RenderingHints.VALUE_TEXT_ANTIALIAS_OFF){
			g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_ON);}else{
				g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
			}
	}
}
